﻿using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using Devonline.AspNetCore;
using Devonline.Core;
using Devonline.Entity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Devonline.AuxiliaryTools.DatabaseTools;

/// <summary>
/// 数据库基本服务
/// </summary>
/// <param name="logger"></param>
public class DatabaseService(ILogger<DatabaseService> logger)
{
    private readonly ILogger<DatabaseService> _logger = logger;

    /// <summary>
    /// 根据数据库表依赖关系, 逐级清空数据库全部表数据, 仅针对 PostgreSQL 数据库
    /// </summary>
    /// <typeparam name="TDbContext">数据库上下文类型</typeparam>
    /// <param name="context">数据库上下文</param>
    /// <returns></returns>
    public async Task ClearAllAsync<TDbContext>(TDbContext context) where TDbContext : DbContext => await ClearAllAsync<TDbContext>(context, DatabaseType.PostgreSQL);
    /// <summary>
    /// 根据数据库表依赖关系, 逐级清空数据库全部表数据
    /// </summary>
    /// <typeparam name="TDbContext">数据库上下文类型</typeparam>
    /// <param name="context">数据库上下文</param>
    /// <param name="databaseType">数据库类型</param>
    /// <returns></returns>
    public async Task ClearAllAsync<TDbContext>(TDbContext context, DatabaseType databaseType) where TDbContext : DbContext
    {
        _logger.LogInformation($"查找依赖数据对象模型并清空数据库开始!");
        await ExecuteDependenciesAsync<TDbContext>(async type => await context.ClearAsync(type, databaseType), true);
        _logger.LogInformation($"查找依赖数据对象模型并清空数据库完成!");
    }

    /// <summary>
    /// 根据数据库表依赖关系, 逐级迁移数据库全部表数据
    /// </summary>
    /// <typeparam name="TSourceDbContext">原数据库上下文类型</typeparam>
    /// <typeparam name="TTargetDbContext">目标数据库上下文类型</typeparam>
    /// <param name="sourceDbContext">原数据库上下文</param>
    /// <param name="targetDbContext">目标数据库上下文</param>
    /// <param name="targetDatabaseType">目标数据库类型</param>
    /// <returns></returns>
    public async Task MigrationAllAsync<TSourceDbContext, TTargetDbContext>(TSourceDbContext sourceDbContext, TTargetDbContext targetDbContext, DatabaseType targetDatabaseType) where TSourceDbContext : DbContext where TTargetDbContext : DbContext
    {
        _logger.LogInformation($"查找依赖数据对象模型并迁移数据库开始!");

        // clear target database all tables
        await ClearAllAsync<TTargetDbContext>(targetDbContext, targetDatabaseType);

        // migration all
        var extensionType = typeof(DbContextExtensions);
        var methodName = nameof(DbContextExtensions.MigrationAsync);
        var typeSourceDbContext = typeof(TSourceDbContext);
        var typeTargetDbContext = typeof(TTargetDbContext);
        await ExecuteDependenciesAsync<TTargetDbContext>(async type =>
        {
            var task = extensionType.InvokeGenericMethod<Task>(methodName, sourceDbContext, new object[] { targetDbContext }, typeSourceDbContext, typeTargetDbContext, type);
            if (task is not null)
            {
                await task;
            }
        });

        _logger.LogInformation($"查找依赖数据对象模型并迁移数据库完成!");
    }
    /// <summary>
    /// 根据数据库表依赖关系, 逐级迁移数据库全部表数据, 仅针对 PostgreSQL 数据库
    /// </summary>
    /// <typeparam name="TSourceDbContext">原数据库上下文类型</typeparam>
    /// <typeparam name="TTargetDbContext">目标数据库上下文类型</typeparam>
    /// <param name="sourceDbContext">原数据库上下文</param>
    /// <param name="targetDbContext">目标数据库上下文</param>
    /// <returns></returns>
    public async Task MigrationAllAsync<TSourceDbContext, TTargetDbContext>(TSourceDbContext sourceDbContext, TTargetDbContext targetDbContext) where TSourceDbContext : DbContext where TTargetDbContext : DbContext
    {
        _logger.LogInformation($"查找依赖数据对象模型并迁移数据库开始!");

        // clear target database all tables
        await ClearAllAsync<TTargetDbContext>(targetDbContext);

        // migration all
        var extensionType = typeof(Devonline.Database.PostgreSQL.ServiceExtensions);
        var methodName = nameof(Devonline.Database.PostgreSQL.ServiceExtensions.MigrationAsync);
        var typeSourceDbContext = typeof(TSourceDbContext);
        var typeTargetDbContext = typeof(TTargetDbContext);
        await ExecuteDependenciesAsync<TTargetDbContext>(async type =>
        {
            var task = extensionType.InvokeGenericMethod<Task>(methodName, sourceDbContext, new object[] { targetDbContext }, typeSourceDbContext, typeTargetDbContext, type);
            if (task is not null)
            {
                await task;
            }
        });

        _logger.LogInformation($"查找依赖数据对象模型并迁移数据库完成!");
    }

    /// <summary>
    /// 获取数据对象模型之间的依赖关系
    /// </summary>
    /// <typeparam name="TDbContext"></typeparam>
    /// <returns></returns>
    public List<DependencyRelationship> GetDependencyRelationshipsFromDbContext<TDbContext>() where TDbContext : DbContext
    {
        var tables = new List<DependencyRelationship>();
        var types = GetTypesFromDbContext<TDbContext>();
        if (types.Count > 0)
        {
            foreach (var type in types)
            {
                tables.Add(new DependencyRelationship(type, GetDependencies(type)));
            }
        }

        return tables;
    }
    /// <summary>
    /// 循环查找依赖项并执行方法
    /// </summary>
    /// <param name="execute">执行的方法</param>
    /// <param name="reverse">是否反向查找依赖, 默认: 否</param>
    /// <returns></returns>
    public async Task ExecuteDependenciesAsync<TDbContext>(Func<Type, Task> execute, bool reverse = false) where TDbContext : DbContext
    {
        _logger.LogInformation("查找依赖数据对象模型并执行方法开始!");
        var dependencyRelationships = GetDependencyRelationshipsFromDbContext<TDbContext>();
        if (dependencyRelationships.Count != 0)
        {
            _logger.LogInformation("查找无依赖数据对象模型并执行方法!");
            var tops = dependencyRelationships.Where(x => x.Dependencies is null || x.Dependencies.Count == 0 || x.Dependencies.All(a => a == x.Type));
            if (tops?.Any() ?? false)
            {
                foreach (var top in tops)
                {
                    _logger.LogInformation($"当前数据对象模型: {top.Type.GetDisplayName()}");
                    await execute(top.Type);
                    top.Confirmed = true;
                }
            }

            _logger.LogInformation("查找有依赖但尚未执行的数据对象模型并执行方法!");
            var dependencies = dependencyRelationships.Where(x => !x.Confirmed).ToList();
            while (dependencies.Count != 0)
            {
                foreach (var dependency in dependencies)
                {
                    try
                    {
                        if (reverse)
                        {
                            //反向逻辑: 当前数据对象模型不存在被依赖项且未确认的情况
                            if (!dependencies.Any(x => x.Type != dependency.Type && x.Dependencies.Contains(dependency.Type)))
                            {
                                _logger.LogInformation($"当前执行的数据对象模型: {dependency.Type.GetDisplayName()}");
                                await execute(dependency.Type);
                                dependency.Confirmed = true;
                            }
                        }
                        else
                        {
                            //正向逻辑: 当前数据对象模型的所有依赖项都已确认的情况下执行方法
                            if (dependencyRelationships.Where(x => x.Type != dependency.Type && dependency.Dependencies.Contains(x.Type)).All(x => x.Confirmed))
                            {
                                _logger.LogInformation($"当前执行的数据对象模型: {dependency.Type.GetDisplayName()}");
                                await execute(dependency.Type);
                                dependency.Confirmed = true;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "执行方法出错, 详情: " + ex.GetMessage());
                        throw;
                    }
                }

                dependencies = dependencyRelationships.Where(x => !x.Confirmed).ToList();
            }
        }

        _logger.LogInformation("查找依赖数据对象模型并执行方法结束!");
    }
    /// <summary>
    /// 获取数据库上下文中的数据对象模型的类型
    /// </summary>
    /// <param name="typeName"></param>
    /// <returns></returns>
    public static List<Type> GetTypesFromDbContext<TDbContext>() where TDbContext : DbContext
    {
        var types = new List<Type>();
        var dbset = nameof(DbSet<EntitySet>);
        var propertyInfos = typeof(TDbContext).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.PropertyType.Name.StartsWith(dbset));
        foreach (var propertyInfo in propertyInfos)
        {
            types.Add(propertyInfo.PropertyType.GetGenericArguments().First());
        }

        return types;
    }
    /// <summary>
    /// 获取数据对象类型的引用类型
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static List<Type> GetDependencies(Type type) => type.GetProperties().Where(x => (!x.PropertyType.IsSampleType()) && x.PropertyType.IsFromType<IEntitySet>() && (!x.IsCollectible) && (!x.HasAttribute<NotMappedAttribute>())).Select(x => x.PropertyType).Distinct().ToList();
}